/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "MCDPDecoder.h"

#include <Winsock2.h>

#include "MAddressIP.h"

//---------------------------------------------------------------------------

MCDPDecoder::MCDPDecoder(void)
{
}

MCDPDecoder::~MCDPDecoder(void)
{
}

//---------------------------------------------------------------------------

CString MCDPDecoder::FindAndGetTypeValueAsString(MCDPPacket* packIn, WORD typeIn)
{
	char valBuff[256];
	int i, cnt, size;
	CString str;
	bool found;
	MTLVRecord *rec;

	str = _T("-");
	if(packIn==NULL) return str;

	memset(valBuff, 0, 256);

	found = false;
	cnt = packIn->xValuesList.GetSize();
	for(i=0; i<cnt && !found; i++){
		rec = packIn->xValuesList.GetByIdx(i);
		if(rec!=NULL){
			if(rec->xType==typeIn){
				found = true;
				size = rec->xLength;
				if(size>4 && rec->xValue!=NULL){
					size -= 4;
					if(size>255) size = 255;
					if(size>0)
						memcpy(&valBuff[0], rec->xValue, size);
				}
			}
		}
	}

	if(found)
		str = valBuff;

	return str;
}

//---------------------------------------------------------------------------

CString MCDPDecoder::GetTypeName(WORD wIn)
{
	CString str;

	switch(wIn){
		case CDPT_DeviceId:
			str = _T("Device ID");
			break;
		case CDPT_Address:
			str = _T("Address");
			break;
		case CDPT_PortId:
			str = _T("Port ID");
			break;
		case CDPT_Capabilities:
			str = _T("Capabilities");
			break;
		case CDPT_Version:
			str = _T("Version");
			break;
		case CDPT_Platform:
			str = _T("CDPT_Platform");
			break;

		case CDPT_Prefixes:
			str = _T("Prefixes");
			break;
		case CDPT_HelloOpt:
			str = _T("Hello Protocol Options");
			break;

		case CDPT_VtpDomain:
			str = _T("VTP Domain");
			break;
		case CDPT_NativeVlan:
			str = _T("Native VLAN");
			break;
		case CDPT_Duplex:
			str = _T("Duplex");
			break;

		case CDPT_VoIP_VLANReply:
			str = _T("VoIP VLAN Reply");
			break;
		case CDPT_VoIP_VLANQuery:
			str = _T("VoIP VLAN Query");
			break;
		case CDPT_Power:
			str = _T("Power");
			break;
		case CDPT_MTU:
			str = _T("MTU");
			break;
		case CDPT_TrustBitmap:
			str = _T("Trust Bitmap");
			break;
		case CDPT_UntrustedCoS:
			str = _T("Untrusted CoS");
			break;
		case CDPT_SystemName:
			str = _T("System Name");
			break;
		case CDPT_SystemOID:
			str = _T("System OID");
			break;
		case CDPT_ManagementAddr:
			str = _T("Management Address");
			break;
		case CDPT_Location:
			str = _T("Location");
			break;

		default:
			str.Format(_T("Unknown (Type %u)"), wIn);
			break;
	}

	return str;
}

//---------------------------------------------------------------------------

CString MCDPDecoder::FormatStringValue(BYTE* dataIn, int sizeIn)
{
	CString str;
	BYTE valBuff[256];
	int size;

	memset(valBuff, 0, 256);
	size = sizeIn;
	if(size>255) size = 255;
	memcpy(&valBuff[0], dataIn, size);

	str = valBuff;

	return str;
}

CString MCDPDecoder::FormatAddressValue(BYTE* dataIn, int sizeIn)
{
	CString str;
	DWORD *dwVal;
	WORD  *wVal;
	int i, cnt, pos, reqSize;
	bool err;

	BYTE  aProtoType;
	BYTE  aProtoLen;
	BYTE  aProto;
	WORD  aAddrLen;
	DWORD aAddrIP;
	MAddressIP addrIP;

	if(sizeIn<4){
		str = _T("- invalid length -");
		return str;
	}

	dwVal = (DWORD*)dataIn;
	cnt = (int)ntohl(*dwVal);

	/*
	only allow for decoding address of 9 bytes as follows:
		BYTE ProtocolType		0x01 = NLPID
		BYTE ProtocolLength		0x01
		BYTE Protocol			0xCC = IP
		WORD AddressLength		0x0004	
		DWORD IPAddress			...
	*/
	reqSize = cnt * 9;
	pos = 4;
	if((pos + reqSize)>sizeIn){
		str = _T("- invalid length -");
		return str;
	}

	str.Empty();
	err = false;
	for(i=0; i<cnt && !err; i++){
		aProtoType = dataIn[pos];
		aProtoLen  = dataIn[pos+1];
		aProto     = dataIn[pos+2];

		wVal = (WORD*)&dataIn[pos+3];
		aAddrLen = ntohs(*wVal);

		dwVal = (DWORD*)&dataIn[pos+5];
		//aAddrIP = ntohl(*dwVal);
		aAddrIP = *dwVal;

		pos += 9;

		if(aProtoType != 0x01)   err = true;
		if(aProtoLen  != 0x01)   err = true;
		if(aProto     != 0xCC)   err = true;
		if(aAddrLen   != 0x0004) err = true;

		if(!err){
			addrIP.Set(aAddrIP);
			if(!str.IsEmpty()) str += _T(", ");
			str += addrIP.GetS();
		}
	}

	return str;
}

CString MCDPDecoder::FormatCapabilitiesValue(BYTE* dataIn, int sizeIn)
{
	CString str;

	str.Empty();

	if(sizeIn<4) return str;

	if(dataIn[3] & CDPC_Router){
		if(!str.IsEmpty()) str += _T(", ");
		str += _T("Router");
	}
	if(dataIn[3] & CDPC_TransparentBridge){
		if(!str.IsEmpty()) str += _T(", ");
		str += _T("Transparent Bridge");
	}
	if(dataIn[3] & CDPC_SourceRouteBridge){
		if(!str.IsEmpty()) str += _T(", ");
		str += _T("Source Route Bridge");
	}
	if(dataIn[3] & CDPC_L2Switch){
		if(!str.IsEmpty()) str += _T(", ");
		str += _T("Layer 2 Switch");
	}
	if(dataIn[3] & CDPC_L3Switch){
		if(!str.IsEmpty()) str += _T(", ");
		str += _T("Layer 3 Switch");
	}
	if(dataIn[3] & CDPC_IGMPSnooping){
		if(!str.IsEmpty()) str += _T(", ");
		str += _T("IGMP Snooping");
	}
	if(dataIn[3] & CDPC_Repeater){
		if(!str.IsEmpty()) str += _T(", ");
		str += _T("Repeater");
	}

	return str;
}

CString MCDPDecoder::FormatNativeVlanValue(BYTE* dataIn, int sizeIn)
{
	CString str;
	WORD *wVal;

	if(sizeIn!=2){
		str = _T("- invalid length -");
		return str;
	}

	wVal = (WORD*)dataIn;
	str.Format(_T("%u"), ntohs(*wVal));
	return str;
}

CString MCDPDecoder::FormatVoIPVlanValue(BYTE* dataIn, int sizeIn)
{
	CString str;
	WORD *wVal;
	BYTE bVal;

	if(sizeIn!=3){
		str = _T("- invalid length -");
		return str;
	}

	bVal = dataIn[0];
	if(bVal!=0x01){
		str = _T("- unknown option -");
		return str;
	}

	wVal = (WORD*)&dataIn[1];
	str.Format(_T("%u"), ntohs(*wVal));
	return str;
}

CString MCDPDecoder::FormatDuplexValue(BYTE* dataIn, int sizeIn)
{
	CString str;

	if(sizeIn!=1){
		str = _T("- invalid length -");
		return str;
	}

	str = dataIn[0]==0 ? _T("Half") : _T("Full");
	return str;
}

CString MCDPDecoder::FormatByteValue(BYTE valIn)
{
	CString str;

	str.Format(_T("0x%02X"), valIn);
	return str;
}

CString MCDPDecoder::FormatRawData(BYTE* dataIn, int sizeIn)
{
	CString str, sss;
	int i, size;

	size = sizeIn;
	if(size>128){
		str = _T("[hex, truncated] ");
		size = 128;
	}
	else{
		str = _T("[hex] ");
	}

	if(size==1){
		str.Format(_T("0x%02X"), dataIn[0]);
		return str;
	}

	for(i=0; i<size; i++){
		if(i<(size-1)) sss.Format(_T("%02X "), dataIn[i]);
		else           sss.Format(_T("%02X"), dataIn[i]);
		str += sss;
	}

	return str;
}

//---------------------------------------------------------------------------

CString MCDPDecoder::GetValueName(MTLVRecord* recIn)
{
	CString str;
	int size;

	str.Empty();
	if(recIn==NULL) return str;

	size = recIn->xLength;
	if(size<4){
		str.Format(_T("- invalid length %u -"), size);
		return str;
	}

	size -= 4;
	if(size==0){
		str = _T("- empty -");
		return str;
	}

	switch(recIn->xType){
		case CDPT_DeviceId:
		case CDPT_Version:
		case CDPT_Platform:
		case CDPT_PortId:
		case CDPT_VtpDomain:
		case CDPT_SystemName:
		case CDPT_Location:
			str = FormatStringValue(recIn->xValue, size);
			break;

		case CDPT_Address:
		case CDPT_ManagementAddr:
			str = FormatAddressValue(recIn->xValue, size);
			break;

		case CDPT_Capabilities:
			str = FormatCapabilitiesValue(recIn->xValue, size);
			break;

		case CDPT_NativeVlan:
			str = FormatNativeVlanValue(recIn->xValue, size);
			break;

		case CDPT_Duplex:
			str = FormatDuplexValue(recIn->xValue, size);
			break;

		case CDPT_TrustBitmap:
		case CDPT_UntrustedCoS:
			if(size==1) str = FormatByteValue(recIn->xValue[0]);
			else        str = _T("- To Be Implemented -");
			break;

		case CDPT_VoIP_VLANReply:
		case CDPT_VoIP_VLANQuery:
			str = FormatVoIPVlanValue(recIn->xValue, size);
			break;

		case CDPT_Prefixes:
		case CDPT_HelloOpt:
		case CDPT_Power:
		case CDPT_MTU:
		case CDPT_SystemOID:
			str = _T("- To Be Implemented -");
			break;

		default:
			str = FormatRawData(recIn->xValue, size);
			break;
	}

	return str;
}

//---------------------------------------------------------------------------
